﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;

using Microsoft.Win32;

namespace King.Extensions {

    public static partial class XObjectExtensions {

        public static string GetLocalName(this XObject xml) {
            var xElement = xml as XElement;
            if (xElement != null)
                return xElement.Name.LocalName;

            var xAttribute = xml as XAttribute;
            if (xAttribute != null)
                return xAttribute.Name.LocalName;

            throw new NotSupportedException();
        }
        public static string GetValue(this XObject xml) {
            var xElement = xml as XElement;
            if (xElement != null)
                return (string)xElement;

            var xAttribute = xml as XAttribute;
            if (xAttribute != null)
                return (string)xAttribute;

            throw new NotSupportedException();
        }
        public static void SetValue(this XObject xml, string value) {
            if (GetValue(xml) == value)
                return;

            var xElement = xml as XElement;
            if (xElement != null) {
                xElement.Value = value;
                return;
            }

            var xAttribute = xml as XAttribute;
            if (xAttribute != null) {
                xAttribute.Value = value;
                return;
            }

            throw new NotSupportedException();
        }
    }

    public static partial class XElementExtensions {

        public static IEnumerable<XObject> GetNodes(this XElement xml, XNamespace ns, Uri xpath) {
            IEnumerable<XElement> result = new XElement[] { xml };

            bool descendents = false;
            foreach (var segment in xpath.Segments.Skip(1).Select(o => o.Trim('/'))) {
                if (string.IsNullOrEmpty(segment)) {
                    descendents = true;
                    continue;
                }

                var isAttribute = segment.StartsWith("@");
                var name = segment.TrimStart('@');

                if (name == "*") {
                    if (descendents) {
                        result = result.Descendants();
                        if (isAttribute)
                            return result.Attributes().Cast<XObject>();
                    } else {
                        if (isAttribute)
                            return result.Attributes().Cast<XObject>();
                        else
                            result = result.Elements();
                    }

                } else {
                    if (descendents) {
                        result = result.Descendants(ns + name);
                        if (isAttribute)
                            return result.Attributes(name).Cast<XObject>();
                    } else {
                        if (isAttribute)
                            return result.Attributes(name).Cast<XObject>();
                        else
                            result = result.Elements(ns + name);
                    }
                }

                descendents = false;
            }

            return result.Cast<XObject>();
        }
        public static XObject GetNode(this XElement xml, XNamespace ns, Uri xpath) {
            return GetNodes(xml, ns, xpath).SingleOrDefault();
        }
        public static IEnumerable<T> GetNodes<T>(this XElement xml, XNamespace ns, Uri xpath) {
            return GetNodes(xml, ns, xpath).Cast<T>();
        }
        public static T GetNode<T>(this XElement xml, XNamespace ns, Uri xpath) {
            return (T)GetNodes(xml, ns, xpath);
        }

        public static IEnumerable<XObject> GetNodes(this XElement xml, XNamespace ns, string xpath) {
            if (xpath.StartsWith("/"))
                xpath = xpath.Substring(1);
            var uri = new Uri("xml://xpath/" + xpath);
            return GetNodes(xml, ns, uri);
        }
        public static XObject GetNode(this XElement xml, XNamespace ns, string xpath) {
            return GetNodes(xml, ns, xpath).SingleOrDefault();
        }
        public static IEnumerable<T> GetNodes<T>(this XElement xml, XNamespace ns, string xpath) {
            return GetNodes(xml, ns, xpath).Cast<T>();
        }
        public static T GetNode<T>(this XElement xml, XNamespace ns, string xpath) {
            return (T)GetNodes(xml, ns, xpath);
        }
    }

    public static partial class StringExtensions {

        public static IEnumerable<string> ReadLines(this TextReader textReader) {
            string line = null;
            while ((line = textReader.ReadLine()) != null)
                yield return line;
        }

        public static IEnumerable<string> SplitCamelCase(this string value) {
            var sb = new StringBuilder();
            foreach (var c in value) {
                if (char.IsUpper(c) && sb.Length > 0) {
                    yield return sb.ToString();
                    sb = new StringBuilder();
                }

                sb.Append(c);
            }

            if (sb.Length > 0)
                yield return sb.ToString();
        }

        public static IEnumerable<string> RootDirectories(this IEnumerable<string> paths) {
            var result = paths.Where(o => paths.All(
                x => o == x || !o.StartsWith(x, StringComparison.InvariantCultureIgnoreCase)));
            return result;
        }
        public static IEnumerable<string> ExceptDirs(this IEnumerable<string> paths, IEnumerable<string> dirs) {
            var result = paths.Where(o => dirs.All(
                x => o == x || !o.StartsWith(x, StringComparison.InvariantCultureIgnoreCase)));
            return result;
        }

        public static Uri NormalizeUrlDirectory(this Uri dir) {
            if (!dir.AbsoluteUri.EndsWith(@"/"))
                return new Uri(dir.AbsoluteUri + @"/");
            return dir;
        }
        public static string NormalizeUrlDirectory(this string dir) {
            if (string.IsNullOrEmpty(dir))
                return dir;
            if (!dir.EndsWith(@"/"))
                dir += @"/";
            return dir;
        }
        public static string NormalizeNewLines(this string source) {
            source = Regex.Replace(source, "\r(?!\n)", Environment.NewLine);
            source = Regex.Replace(source, "(?<!\r)\n", Environment.NewLine);
            return source;
        }

        public static string Remove(this string source, string target) {
            return source.Replace(target, string.Empty);
        }
        public static bool CaseInsensitveEquals(this string source, string target) {
            return source.ToLower().Equals(target.ToLower());
        }

        public static string Substitute(this string source, params object[] arguments) {
            var result = string.Format(source, arguments);
            return result;
        }

        public static Queue<string> ToLinesQueue(this string source) {
            if (source == null)
                return new Queue<string>();

            return source.StringSplit(Environment.NewLine).ToQueue();
        }

        public static Stream ToStream(this string source) {
            byte[] data = Encoding.UTF8.GetBytes(source);
            MemoryStream memoryStream = new MemoryStream();
            memoryStream.Write(data, 0, data.Length);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return memoryStream;
        }

        public static string Indent(this string @string, int count) {
            return Indent(@string, count, 1);
        }
        public static string Indent(this string @string, int tabSize, int tabCount) {
            var indent = "".PadLeft(tabCount * tabSize);
            return indent + @string.Replace(Environment.NewLine, Environment.NewLine + indent).TrimEnd(' ');
        }
    }

    public static partial class QueueExtensions {

        public static Queue<T> ToQueue<T>(this IEnumerable<T> source) {
            var queue = new Queue<T>();
            queue.EnqueueRange(source);
            return queue;
        }
        public static void EnqueueRange<T>(this Queue<T> queue, IEnumerable<T> range) {
            foreach (var element in range)
                queue.Enqueue(element);
        }
        public static IEnumerable<T> DequeueWhile<T>(this Queue<T> queue, Func<T, bool> predicate) {
            while (queue.Count > 0 && predicate(queue.Peek()))
                yield return queue.Dequeue();
        }
    }

    public static partial class StackExtensions {

        public static Stack<T> ToStack<T>(this IEnumerable<T> source) {
            var Stack = new Stack<T>();
            Stack.PushRange(source);
            return Stack;
        }
        public static void PushRange<T>(this Stack<T> stack, IEnumerable<T> range) {
            foreach (var element in range)
                stack.Push(element);
        }
        public static IEnumerable<T> PopWhile<T>(this Stack<T> stack, Func<T, bool> predicate) {
            while (stack.Count > 0 && predicate(stack.Peek()))
                yield return stack.Pop();
        }
        public static T PopOrDefault<T>(this Stack<T> stack) {
            if (stack.None())
                return default(T);
            return stack.Pop();
        }
    }

    public static partial class EnumerableExtensions {

        public static IEnumerable<T> SelectMany<T>(this IEnumerable<IEnumerable<T>> source) {
            return source.SelectMany(o => o);
        }

        public static T MaxOr<T>(this IEnumerable<T> @this, T @default) {
            if (@this.None())
                return @default;
            return @this.Max();
        }
        public static V MaxOr<T, V>(this IEnumerable<T> @this, Func<T, V> selector, V @default) {
            if (@this.None())
                return @default;
            return @this.Max(selector);
        }
        public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, T ifNull) {
            if (source.None(predicate))
                return ifNull;

            return source.First(predicate);
        }
        public static T FirstOr<T>(this IEnumerable<T> source, T ifNull) {
            if (source.None())
                return ifNull;

            return source.First();
        }
        public static T SingleOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, T ifNull) {
            if (source.None())
                return ifNull;

            return source.Single(predicate);
        }
        public static T SingleOr<T>(this IEnumerable<T> source, T ifNull) {
            if (source.None())
                return ifNull;

            return source.Single();
        }

        // params
        public static IEnumerable<T> Concat<T>(this IEnumerable<T> source, params T[] set) {
            return source.Concat((IEnumerable<T>)set);
        }
        public static IEnumerable<T> Union<T>(this IEnumerable<T> source, params T[] set) {
            return source.Union((IEnumerable<T>)set);
        }

        // where
        public static IEnumerable<T> WhereIs<T>(this IEnumerable source) {
            return source.Cast<object>().Where(o => o is T).Cast<T>();
        }
        public static IEnumerable<T> ExceptFor<T>(this IEnumerable<T> @this, params T[] exceptions) {
            return @this.Except((IEnumerable<T>)exceptions);
        }
        public static IEnumerable<T> ExceptWhere<T>(this IEnumerable<T> @this, Func<T, bool> predicate) {
            return @this.Where(o => !predicate(o));
        }
        public static bool AnyDuplicates<T>(this IEnumerable<T> @this) {
            return @this.AnyDuplicates(o => o);
        }
        public static bool AnyDuplicates<T, TKey>(this IEnumerable<T> @this, Func<T, TKey> keySelector) {
            return @this.Select(o => keySelector(o)).Distinct().Count() != @this.Count();
        }

        // take
        public static IEnumerable<T> TakeWhileInclusive<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
            foreach (var o in source) {
                yield return o;
                if (!predicate(o))
                    break;
            }
            throw new ArgumentException();
        }

        // stringJoin
        public static string StringJoin<T>(this IEnumerable<T> source) {
            return source.StringJoin("", "", o => o, "");
        }
        public static string StringJoin<T>(this IEnumerable<T> source, string separator) {
            return source.StringJoin("", separator, o => o, "");
        }
        public static string StringJoin<T>(this IEnumerable<T> source, string prefix, string separator, string suffix) {
            return source.StringJoin(prefix, separator, o => o, suffix);
        }
        public static string StringJoin<T>(this IEnumerable<T> source, string separator, Func<T, object> selector) {
            return source.StringJoin("", separator, selector, "");
        }
        public static string StringJoin<T>(this IEnumerable<T> source, string prefix, string separator, Func<T, object> selector, string suffix) {
            if (source == null)
                throw new ArgumentNullException();
            if (source.None())
                return string.Empty;
            var join = source.WhereNotDefault().Select(o => selector(o).ToString()).ToArray();
            var result = prefix + string.Join(separator, join) + suffix;
            return result;
        }

        // count
        public static bool OnlyOne<T>(this IEnumerable<T> source) {
            return source.CountIs(1);
        }
        public static T OnlyOneOrDefault<T>(this IEnumerable<T> source) {
            return source.CountIs(1) ? source.Single() : default(T);
        }
        public static bool MoreThanOne<T>(this IEnumerable<T> source) {
            return source.MoreThan(1);
        }
        public static bool CountIs<T>(this IEnumerable<T> source, int count) {
            return source.Count() == count;
        }
        public static bool MoreThan<T>(this IEnumerable<T> source, int count) {
            for (int i = 0; i < count; i++) {
                if (source.None())
                    return false;
                source = source.Skip(1);
            }

            return source.Any();
        }

        // equality
        public static bool IsEqualTo<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target, IEqualityComparer<TSource> comparer) {
            if (source == target)
                return true;

            if (source == null || target == null)
                return false;

            if (comparer == null)
                comparer = EqualityComparer<TSource>.Default;

            var sourceEnumerator = source.GetEnumerator();
            var targetEnumerator = target.GetEnumerator();

            try {
                while (true) {
                    bool sourceHasValue = sourceEnumerator.MoveNext();
                    bool targetHasValue = targetEnumerator.MoveNext();

                    if (sourceHasValue != targetHasValue)
                        return false;

                    if (sourceHasValue == false)
                        return true;

                    if (!comparer.Equals(sourceEnumerator.Current, targetEnumerator.Current))
                        return false;
                }

            } finally {
                sourceEnumerator.Dispose();
                targetEnumerator.Dispose();
            }
        }
        public static bool IsEqualTo<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target) {
            return IsEqualTo(source, target, null);
        }
        public static bool IsEqualTo(this IEnumerable source, IEnumerable target) {
            return IsEqualTo<object>(source.OfType<object>(), target.OfType<object>());
        }

        // zip
        public static IEnumerable<TResponse> Zipper<TOutter, TInner, TResponse>(
            this IEnumerable<TOutter> outter,
            IEnumerable<TInner> inner,
            Func<TOutter, TInner, TResponse> resultSelector) {

            var outterArray = outter.ToList();
            var innerArray = inner.ToList();
            if (outterArray.Count != innerArray.Count)
                throw new ArgumentException();

            var outterEnumerator = outterArray.GetEnumerator();
            var innerEnumerator = innerArray.GetEnumerator();

            while (outterEnumerator.MoveNext() && innerEnumerator.MoveNext())
                yield return resultSelector(outterEnumerator.Current, innerEnumerator.Current);
        }

        // distict
        public static IEnumerable<T> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) {
            return source.GroupBy(o => keySelector(o)).Select(o => o.First());
        }

        // index
        public static int? FirstIndex<T>(this IEnumerable<T> source, T element) {
            return source.FirstIndex(o => EqualityComparer<T>.Default.Equals(o, element));
        }
        public static int? FirstIndex<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
            var result = source.Select((e, i) => new { e, i }).FirstOrDefault(o => predicate(o.e));
            if (result == null)
                return null;
            return result.i;
        }

        // commonSets
        public static IEnumerable<TSet> GreatestCommonSets<TSet>(this IEnumerable<TSet> sets, Func<TSet, TSet, bool> contains) {
            var result = sets.Where(lhs => sets.All(rhs => object.Equals(lhs, rhs) || !contains(rhs, lhs))).ToArray();
            return result;
        }
        public static IEnumerable<TSet> SmallestCommonSets<TSet>(this IEnumerable<TSet> sets, Func<TSet, TSet, bool> contains) {
            var result = sets.Where(lhs => sets.None(rhs => contains(lhs, rhs)));
            return result;
        }
    }

    public static partial class ObjectExtensions {

        // toLinkList
        public static IEnumerable<T> ToLinkList<T>(this T source, Func<T, T> next, Func<T, bool> takeWhile) {
            return source.ToLinkList(next).TakeWhile(takeWhile);
        }

        public static object ToArray(this IEnumerable source, Type elementType) {
            var array = source.Cast<object>().ToArray();
            var result = Array.CreateInstance(elementType, array.Length);
            Array.Copy(array, result, array.Length);
            return result;
        }
    }

    public static class UriComparer {
        private class IgnoreCaseComparer : IEqualityComparer<Uri> {
            public bool Equals(Uri x, Uri y) {
                return x.AbsoluteUri.ToLower().Equals(y.AbsoluteUri.ToLower());
            }
            public int GetHashCode(Uri obj) {
                return obj.AbsoluteUri.ToLower().GetHashCode();
            }
        }

        public static readonly IEqualityComparer<Uri> IgnoreCase = new IgnoreCaseComparer();
    }
    public static partial class UriExtensions {

        public static Uri RelativePathToRelativeUrl(this string source) {
            return new Uri(source.Replace("\\", "/"), UriKind.RelativeOrAbsolute);
        }
        public static Uri RelativeUrlToRelativePath(this Uri source) {
            var str = source.ToString();
            var regex = Regex.Match(str, @"^(?<backup>(\.\./)*)(?<path>.*)$");
            var backup = regex.Get("backup").Replace("/", "\\");
            var path = regex.Get("path");

            var relPath = new Uri(new Uri("http://foo"), path).LocalPath.TrimStart('/');
            var relPathUri = new Uri(backup + relPath, UriKind.RelativeOrAbsolute);
            return relPathUri;
        }
        public static Uri MakeRelativeUriPath(this Uri source, Uri target) {
            return source.MakeRelativeUri(target).RelativeUrlToRelativePath();
        }
        public static string MakeRelativePath(this Uri source, string target) {
            return source.MakeRelativePath(new Uri(target));
        }
        public static string MakeRelativePath(this Uri source, Uri target) {
            return source.MakeRelativeUriPath(target).ToString().Replace("/", "\\");
        }
        public static Uri GetDirectory(this Uri source) {
            return new Uri(source, ".");
        }
        public static Uri GetParentDirectory(this Uri source) {
            var result = new Uri(source, "..");
            if (result.Equals(source))
                return null;
            return result;
        }
        public static string GetName(this Uri source) {
            return source.Segments.Last().Trim('/');
        }
        public static bool IsDirectory(this Uri source) {
            return source.Equals(source.GetDirectory());
        }
        public static bool IsSubUrlOrEqual(this Uri source, Uri target) {
            return IsSubUrl(source, target) || source.Equals(target);
        }
        public static Uri CommonParent(this Uri source, Uri target) {
            if (source == null)
                return new Uri(target, ".");

            var relUrl = source.MakeRelativeUri(target);
            var rel = relUrl.ToString().TakeWhile(o => "./".Contains(o)).StringJoin();

            var result = new Uri(source, rel);
            return result;
        }
        public static bool IsSubUrl(this Uri source, Uri target) {
            if (source.Equals(target))
                return false;

            var relUrl = source.MakeRelativeUri(target);
            var rel = relUrl.ToString();
            var result = rel.EndsWith("../") || string.IsNullOrEmpty(rel) || rel.Equals("./");
            return result;
        }
        public static bool CaseInsensitiveEquals(this Uri source, Uri target) {
            return source.AbsoluteUri.ToLower() == target.AbsoluteUri.ToLower();
        }
    }

    public static partial class DictionaryExtensions {

        public static IEnumerable<V> AddRange<K, V>(this Dictionary<K, V> source, IEnumerable<V> values, Func<V, K> keySelector) {
            foreach (var value in values)
                source.Add(keySelector(value), value);
            return values;
        }
        public static Dictionary<K, V> AddRange<T, K, V>(this Dictionary<K, V> source,
            IEnumerable<T> target, Func<T, K> keySelector, Func<T, V> valueSelector) {
            foreach (var o in target) {
                var key = keySelector(o);
                var value = valueSelector(o);
                source.Add(key, value);
            }
            return source;
        }
        public static IEnumerable<V> GetRange<K, V>(this Dictionary<K, V> source, IEnumerable<K> keys) {
            IEnumerable<K> keysWithoutValues;
            return source.GetRange(keys, out keysWithoutValues);
        }
        public static IEnumerable<V> GetRange<K, V>(this Dictionary<K, V> source, IEnumerable<K> keys, out IEnumerable<K> keysWithoutValues) {
            var result = new List<V>();
            var keysWithoutValuesList = new List<K>();
            foreach (var key in keys) {
                var cached = default(V);
                if (!source.TryGetValue(key, out cached))
                    keysWithoutValuesList.Add(key);
                else
                    result.Add(cached);
            }
            keysWithoutValues = keysWithoutValuesList;
            return result;
        }
    }

    public static class RegistryExtensions {

        public static RegistryKey OpenSubKeyPath(this RegistryKey key, string path) {
            return key.OpenSubKeyPath(path, false);
        }
        public static RegistryKey OpenSubKeyPath(this RegistryKey key, string path, bool writeable) {
            var paths = path.StringSplit("/");
            foreach (var dir in paths) {
                key = key.OpenSubKey(dir, writeable);
                if (key == null)
                    return null;
            }
            return key;
        }
    }

    public static class GrabBagExtensions {

        public class EqualityComparer<T, TKey> : IEqualityComparer<T> {

            #region Private Data Members
            private Func<T, TKey> m_keySelector;
            #endregion

            #region Constructor
            internal EqualityComparer(Func<T, TKey> keySelector) {
                m_keySelector = keySelector;
            }
            #endregion

            #region IEqualityComparer<T> Members
            public bool Equals(T x, T y) {
                return m_keySelector(x).Equals(m_keySelector(y));
            }
            public int GetHashCode(T obj) {
                return m_keySelector(obj).GetHashCode();
            }
            #endregion
        }

        #region ValueType
        public static bool IsDefault<T>(this T @struct) where T : struct {
            return @struct.Equals(default(T));
        }
        #endregion

        #region String
        public static Uri ToUri(this string @string) {
            if (@string == null)
                return null;
            return new Uri(@string);
        }
        public static string EmptyToNull(this string @string) {
            if (@string == string.Empty)
                return null;
            return @string;
        }
        public static string SubstringBefore(this string source, string target) {
            if (string.IsNullOrEmpty(target))
                return source;
            var index = source.IndexOf(target);
            if (index == -1)
                return source;
            return source.Substring(0, index);
        }
        public static string SubstringAfter(this string @string, string target) {
            if (string.IsNullOrEmpty(target))
                return @string;
            var index = @string.IndexOf(target);
            if (index == -1)
                return @string;
            return @string.Substring(index + target.Length);
        }
        public static string SubstringAfterLast(this string @string, string target) {
            if (string.IsNullOrEmpty(target))
                return @string;
            var index = @string.LastIndexOf(target);
            if (index == -1)
                return @string;
            return @string.Substring(index + target.Length);
        }
        public static bool IsEmpty(this StringBuilder stringBuilder) {
            return stringBuilder.Length == 0;
        }
        public static void AppendLine(this StringBuilder stringBuilder, object argument) {
            if (argument == null)
                return;
            var toString = argument.ToString();
            if (string.IsNullOrEmpty(toString))
                return;
            stringBuilder.AppendLine(toString);
        }
        public static void AppendLine(this StringBuilder stringBuilder, string format, params object[] arguments) {
            stringBuilder.Append(format.Substitute(arguments) + Environment.NewLine);
        }
        #endregion

        #region IEnumerable
        public static IEnumerable<object> ToEnumerableOfObject(this IEnumerable source) {
            if (source == null)
                return Enumerable.Empty<object>();

            return source.Cast<object>();
        }
        private static IEnumerable Cast<T>(this IEnumerable @this, Type elementType) {
            if (typeof(T) != elementType) {
                return (IEnumerable)((MethodInfo)MethodInfo.GetCurrentMethod())
                        .MakeGenericMethod(elementType)
                        .Invoke(null, new object[] { @this, elementType });
            }

            return (IEnumerable)@this.Cast<T>();
        }
        public static IEnumerable<T> CastOrEmpty<T>(this IEnumerable source) {
            if (source == null)
                return Enumerable.Empty<T>();
            return source.Cast<T>();
        }
        #endregion

        #region IEnumerable<T> (set)
        public static IEnumerable<T> Flatten<TKey, T>(this IEnumerable<IGrouping<TKey, T>> source) {
            if (source == null)
                yield break;

            foreach (var group in source) {
                foreach (var element in group)
                    yield return element;
            }
        }
        public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> @this) {
            foreach (var element in @this)
                foreach (var subElement in element)
                    yield return subElement;
        }
        public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> @this, T epilog) {
            foreach (var enumerable in @this) {
                foreach (var element in enumerable)
                    yield return element;
                yield return epilog;
            }
        }
        public static IEnumerable<T> Intersect<T, TKey>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, TKey> keySelector) {
            return first.Intersect(second, new EqualityComparer<T, TKey>(keySelector));
        }
        #endregion

        #region IEnumerable<T> (compare)
        public static IEnumerable<T> FirstDuplicate<T>(this IEnumerable<T> source) where T : IComparable {
            return FirstDuplicate(source, o => o);
        }
        public static IEnumerable<T> FirstDuplicate<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) where TKey : IComparable {
            var ordered = source.OrderBy(o => keySelector(o)).ToArray();

            if (ordered.Length < 2)
                yield break;

            T previous = ordered.FirstOrDefault();

            foreach (var element in ordered.Skip(1)) {

                if (keySelector(element).CompareTo(keySelector(previous)) != 0) {
                    yield return previous;
                    yield return element;
                    yield break;
                }

                previous = element;
            }
        }
        #endregion

        #region IEnumerable<T> (basic)
        public static IEnumerable<object> AsEnumerableOfObject<T>(this IEnumerable<T> @this) {
            foreach (var e in @this)
                yield return e;
        }
        public static IEnumerable<T> Normailze<T>(this IEnumerable<T> @this) {
            if (@this == null)
                return new T[] { };
            return @this;
        }
        public static IEnumerable<TResult> SelectNow<T, TResult>(this IEnumerable<T> source, Func<T, TResult> selector) {
            return source.Select(o => selector(o)).ToArray();
        }
        public static IEnumerable<TResult> SelectPrefix<T, TResult>(this IEnumerable<T> @this, TResult prefix, Func<T, TResult> selector) {
            if (@this.Any())
                yield return prefix;

            foreach (var element in @this)
                yield return selector(element);
        }
        public static IEnumerable<T> Assert<T>(this IEnumerable<T> @this, Func<T, bool> assert) {
            foreach (var element in @this) {
                Debug.Assert(assert(element));
                yield return element;
            }
        }
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> @this, Func<T, T> touch) {
            foreach (var element in @this)
                yield return touch(element);
        }
        #endregion

        #region IEnumerable<T> (misc)
        public static IEnumerable<object> InvokeList(this MulticastDelegate d, params object[] arguments) {
            if (d == null)
                return Enumerable.Empty<object>();
            return d.GetInvocationList().Select(o => o.DynamicInvoke(arguments));
        }
        public static List<TResult> ToList<T, TResult>(this IEnumerable<T> source, Func<T, TResult> selector) {
            return source.Select(selector).ToList();
        }
        public static TResult[] ToArray<T, TResult>(this IEnumerable<T> source, Func<T, TResult> selector) {
            return source.Select(selector).ToArray();
        }
        #endregion

        #region IEnumerable<T> (Sequence, Ascending, Walk, LeftZip, LeftMatch, LeftSequentialMatch)
        public static TResult SelectFirstOrDefault<T, TResult>(
            this IEnumerable<T> source,
            Func<T, TResult> projection,
            Func<TResult, bool> predicate,
            TResult @default) {

            foreach (var e in source) {
                var p = projection(e);
                if (predicate(p))
                    return p;
            }

            return @default;
        }
        public static IEnumerable<KeyValuePair<int, T>> Sequence<T>(this IEnumerable<T> @this) {
            return Sequence<T>(@this, 0);
        }
        public static IEnumerable<KeyValuePair<int, T>> Sequence<T>(this IEnumerable<T> @this, int start) {

            int i = start;
            foreach (var element in @this)
                yield return new KeyValuePair<int, T>(i++, element);
        }
        public static IEnumerable<TResult> Sequence<T, TResult>(this IEnumerable<T> @this, Func<T, int, TResult> resultSelector) {

            int i = 0;
            foreach (var element in @this)
                yield return resultSelector(element, i++);
        }
        public static IEnumerable<T> Ascending<T, K>(this IEnumerable<T> source, Func<T, K> keySelector, Func<T, bool> exceptIf) where K : IComparable {

            var queue = new Queue<T>(source);

            T previousElement = default(T);
            while (queue.Any()) {
                var element = queue.Dequeue();
                if (!exceptIf(element) && previousElement != null) {
                    if (keySelector(previousElement).CompareTo(keySelector(element)) > 0)
                        throw new ArgumentException("Elements are not in ascending order.");
                }
                if (!exceptIf(element))
                    previousElement = element;
                yield return element;
            }
        }
        public static IEnumerable<TResult> Walk<T, S, TResult>(this IEnumerable<T> @this, IEnumerable<S> enumeration, Func<T, S, TResult> result) {
            var tEnumerator = @this.GetEnumerator();
            var sEnumerator = enumeration.GetEnumerator();

            while (tEnumerator.MoveNext()) {
                if (!sEnumerator.MoveNext())
                    throw new InvalidOperationException();
                yield return result(tEnumerator.Current, sEnumerator.Current);
            }

            if (sEnumerator.MoveNext())
                throw new InvalidOperationException();
        }
        public static IEnumerable<TResponse> LeftZip<TOutter, TInner, TResponse>(
            this IEnumerable<TOutter> outter,
            IEnumerable<TInner> inner,
            Func<TOutter, TInner, TResponse> resultSelector) {

            return LeftZip(outter, inner, o => default(TInner), resultSelector);
        }
        public static IEnumerable<TResponse> LeftZip<TOutter, TInner, TResponse>(
            this IEnumerable<TOutter> outter,
            IEnumerable<TInner> inner,
            Func<TOutter, TInner> defaultSelector,
            Func<TOutter, TInner, TResponse> resultSelector) {

            return LeftZip(
                outter,
                inner,
                i => !object.Equals(i, default(TOutter)),   // hasValue
                defaultSelector,
                resultSelector);
        }
        public static IEnumerable<TResponse> LeftZip<TOutter, TInner, TResponse>(
            this IEnumerable<TOutter> outter,
            IEnumerable<TInner> inner,
            Func<TOutter, bool> hasValue,
            Func<TOutter, TInner> defaultSelector,
            Func<TOutter, TInner, TResponse> resultSelector) {

            var innerEnumerator = inner.GetEnumerator();

            foreach (var o in outter) {

                if (!hasValue(o)) {
                    innerEnumerator.MoveNext();

                } else {

                    TInner i;
                    if (innerEnumerator.MoveNext())
                        i = innerEnumerator.Current;
                    else
                        i = defaultSelector(o);

                    yield return resultSelector(o, i);
                }
            }
        }
        public static IEnumerable<TResponse> LeftZip<TOutter, TInner, TResponse>(
            this IEnumerable<TOutter> outter,
            IEnumerable<TInner> inner0,
            IEnumerable<TInner> inner1,
            Func<TOutter, bool> hasValue,
            Func<TOutter, TInner> defaultSelector0,
            Func<TOutter, TInner> defaultSelector1,
            Func<TOutter, TInner, TInner, TResponse> resultSelector) {

            var inner0Enumerator = inner0.GetEnumerator();
            var inner1Enumerator = inner1.GetEnumerator();

            foreach (var o in outter) {

                if (!hasValue(o)) {
                    inner0Enumerator.MoveNext();
                    inner1Enumerator.MoveNext();

                } else {
                    TInner i0;
                    if (inner0Enumerator.MoveNext())
                        i0 = inner0Enumerator.Current;
                    else
                        i0 = defaultSelector0(o);

                    TInner i1;
                    if (inner1Enumerator.MoveNext())
                        i1 = inner1Enumerator.Current;
                    else
                        i1 = defaultSelector1(o);

                    yield return resultSelector(o, i0, i1);
                }
            }
        }
        public static IEnumerable<TResult> LeftMatch<TOuter, TInner, TKey, TResult>(
            this IEnumerable<TOuter> outer,
            IEnumerable<TInner> inner,
            Func<TOuter, TKey> outerKeySelector,
            Func<TInner, TKey> innerKeySelector,
            Func<TOuter, TInner, TResult> resultSelector) {

            return outer.GroupJoin(         // outter
                inner,                      // inner
                o => outerKeySelector(o),   // outter key selector
                o => innerKeySelector(o),   // inner key selector
                (o, i) => resultSelector(o, i.FirstOrDefault()));
        }
        public static IEnumerable<TResult> LeftSequentialMatch<TOuter, TInner, TKey, TResult>(
            this IEnumerable<TOuter> outer,
            IEnumerable<TInner> inner,
            Func<TOuter, TKey> outerKeySelector,
            Func<TInner, TKey> innerKeySelector,
            Func<TOuter, TInner, TResult> resultSelector) {

            return outer.Sequence((o, i) => new { Value = o, SequenceNumber = i })
                // join the sequences using user supplied key selectors
                .GroupJoin(inner.Sequence((o, i) => new { Value = o, SequenceNumber = i }),
                    o => outerKeySelector(o.Value),
                    o => innerKeySelector(o.Value),
                    (o, i) => new { Outer = o, Inner = i.FirstOr(new { Value = default(TInner), SequenceNumber = -1 }) })

                // order by the SequenceNumber
                .OrderBy(o => o.Inner.SequenceNumber)

                // only return elements where the inner sequence number   
                // is greater than the previous inner sequence number
                // except if there is no matching inner element 
                //  (e.g. the "left" element has a null right element)
                .Ascending(o => o.Outer.SequenceNumber, o => o.Inner.SequenceNumber == -1)

                .OrderBy(o => o.Outer.SequenceNumber)

                // select the values using the user supplied resultSelector
                .Select(o => resultSelector(o.Outer.Value, o.Inner.Value));
        }
        #endregion

        #region IList
        public static void AddRange(this IList list, IEnumerable range) {
            foreach (var value in range)
                list.Add(value);
        }
        #endregion

        #region Queue<T>
        public static T PopPush<T>(this Stack<T> stack, T element) {
            var o = stack.Pop();
            stack.Push(element);
            return o;
        }
        #endregion

        #region Type (generics, default, activation)
        public static IEnumerable<IGrouping<T, T>> GroupBy<T>(
            this IEnumerable<T> source, Func<T, T, bool> isChild) {

            foreach (var parent in source) {
                var children = new List<T>();

                foreach (var child in source) {
                    if (isChild(parent, child))
                        children.Add(child);

                    if (children.Count > 0)
                        yield return children.GroupBy(o => parent).First();
                }
            }
        }
        public static IEnumerable<IGrouping<TKey, T>> PreOrder<T, TKey>(
            this IEnumerable<T> source, Func<T, T, bool> isChild, Func<T, int> depth) {

            //var nodes = source.GroupBy(isChild);
            //var lookup = nodes.ToDictionary(o => o.Key);
            //var roots = nodes.GroupBy(o => depth(o.Key)).OrderBy(o => o.Key).First();

            //var stack = new Stack<T>();
            //stack.PushRange(roots);

            //while (stack.Count != 0) {
            //    var t = stack.Pop();

            //    yield return t;
            //}
            throw new NotImplementedException();
        }
        #endregion

        #region DbCommand
        public static string GetSql(this DbCommand command) {
            var sql = command.CommandText;
            var arguments = command.Parameters.Cast<DbParameter>().ToArray();
            foreach (var argument in arguments)
                sql = sql.Replace(
                    argument.ParameterName,
                    argument.Value == null ? "(null)" : argument.Value.ToString());
            return sql;
        }
        #endregion

        #region Misc
        public static object Factory<T>() where T : new() {
            return new T();
        }
        #endregion

        #region Dictionary<K,V>, IEnumerable<KeyValuePair<K,V>>
        public static IEnumerable<KeyValuePair<K, V>>
            ToPair<T, K, V>(
            this IEnumerable<T> @this,
            Func<T, K> selectKey,
            Func<T, V> selectValue) {

            foreach (var element in @this)
                yield return new KeyValuePair<K, V>(selectKey(element), selectValue(element));
        }
        public static IEnumerable<KeyValuePair<OutK, OutV>>
            SelectPair<InK, InV, OutK, OutV>(
            this IEnumerable<KeyValuePair<InK, InV>> @this,
            Func<InK, OutK> selectKey,
            Func<InV, OutV> selectValue) {

            foreach (var pair in @this)
                yield return new KeyValuePair<OutK, OutV>(selectKey(pair.Key), selectValue(pair.Value));
        }
        public static Dictionary<K, V>
            ToDictionary<K, V>(
            this IEnumerable<KeyValuePair<K, V>> @this) {

            return @this.ToDictionary(o => o.Key, o => o.Value);
        }
        #endregion
    }
}